/*
 * SystemSnapshot.java
 * 
 * Created by demory on Mar 18, 2009, 9:38:50 PM
 * 
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.fpdev.apps.admin.analysis;

import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.media.MediaLocator;
import org.fpdev.core.algo.IntervalTree;
import org.fpdev.core.transit.Route;
import org.fpdev.core.transit.SubRoute;
import org.fpdev.apps.admin.AdminClient;
import org.fpdev.apps.admin.gui.map.Animation;
import org.fpdev.apps.admin.gui.map.MovieRecorder;
import org.fpdev.core.transit.CalendarService;
import org.fpdev.util.FPUtil;

/**
 *
 * @author demory
 */
public class Visualizer {

  private AdminClient ac_;
  private Animation animation_;

  private Map<String, IntervalTree> runTrees_;

  private CalendarService service_;


  public Visualizer(AdminClient ac) {
    ac_ = ac;

    runTrees_ = new HashMap<String, IntervalTree>(); // maps CalSvc ID to i-tree
  }

  public Animation getAnimation() {
    return animation_;
  }

  public void setService(CalendarService service) {
    service_ = service;
    if(!runTrees_.containsKey(service_.getID())) buildIntervalTree(service_);
  }

  private IntervalTree buildIntervalTree(CalendarService service) {

    ac_.msg("building tree");
    Collection<Route> rtes = ac_.getEngine().getRoutes().getCollection(ac_.getActiveScenario());
    Set<VisualizedRun> runs = new HashSet<VisualizedRun>();

    for(Route rte : rtes) {
      for(SubRoute sub : rte.getSubRoutes()) {
        if(!sub.hasTable(service)) continue;
        for(int r = 1; r <= sub.getTable(service).runCount(); r++) {
          runs.add(new VisualizedRun(sub, service, r));
        }
      }
    }

    IntervalTree tree = new IntervalTree(runs);
    runTrees_.put(service.getID(), tree);
    return tree;
  }

  public void refreshSnapshot(int stime) {
    //ac_.msg("refreshing snapshot");

    IntervalTree iTree = runTrees_.get(service_.getID());

    Set<VisualizedRun> results = iTree.query(stime);
    //ac_.msg("found "+results.size());
    ac_.getGUI().getMapPanel().getDrawItems().removeFromPrefix("snap_");

    Set<VisualizedVehicle> visibleVVs = new HashSet<VisualizedVehicle>();
    for(VisualizedRun vr : results) {
      //av_.msg(ri.getSubRoute().getMasterID()+", run "+ri.getRun()+" ("+FPUtil.sTimeToStr(ri.getLower())+" to "+FPUtil.sTimeToStr(ri.getUpper())+")");
      
      VisualizedVehicle vv = null;
      String key = "snap_"+vr.getSubRoute().getMasterID()+"_"+vr.getRun();

      switch(vr.getSubRoute().getPath().getType()) {

        case BUS:
          double locProp = vr.getLocationAtTime(service_, vr.getRun(), stime);
          Point2D.Double pt[] = vr.getSubRoute().getPath().getPoint(locProp); //getLocationAtTime(service_, ri.getRun(), stime);
          if(pt == null)
            System.out.println("WARNING: out of range: "+vr.getSubRoute().getMasterID()+", run "+vr.getRun()+", ("+FPUtil.sTimeToStr(vr.getLower())+" to "+FPUtil.sTimeToStr(vr.getUpper())+"), q time="+FPUtil.sTimeToStr(stime));
          else {
            Point2D.Double[] pts = new Point2D.Double[1];
            pts[0] = pt[0];
            double[] thetas = new double[1];
            thetas[0] = FPUtil.getTheta(pt[1].x, pt[1].y);
            vv = new VisualizedVehicle(ac_.getGUI(), vr, pts, thetas, 24, 12);
          }
          break;
        case METRO:
          locProp = vr.getLocationAtTime(service_, vr.getRun(), stime);
          double pathLenW = vr.getSubRoute().getPath().lengthFt(false);
          int vehCount = 3, vehLenPx = 12, bufferPx = 2;

          int spanPx = (vehLenPx + bufferPx);
          double spanW = ac_.getGUI().getMapPanel().getCanvas().getCC().dxToWorld(spanPx); // span between two adjacent "cars" (center-to-center) in world coords
          double trainSpanW = spanW * (double)(vehCount-1);
          double spanP = spanW / pathLenW;
          double trainSpanP = trainSpanW / pathLenW;

          List<Point2D.Double> ptList = new ArrayList<Point2D.Double>();
          List<Double> thetaList = new ArrayList<Double>();

          double curLocP = locProp + trainSpanP/2;
          for (int i = 0; i < vehCount; i++) {
            if(curLocP >= 0 && curLocP <= 1) {
              pt = vr.getSubRoute().getPath().getPoint(curLocP);
              ptList.add(pt[0]);
              thetaList.add(FPUtil.getTheta(pt[1].x, pt[1].y));
            }
            curLocP -= spanP;
          }
          Point2D.Double[] pts = new Point2D.Double[ptList.size()];
          double[] thetas = new double[ptList.size()];
          for (int i = 0; i < ptList.size(); i++) {
            pts[i] = ptList.get(i);
            thetas[i] = thetaList.get(i);
          }
          vv = new VisualizedVehicle(ac_.getGUI(), vr, pts, thetas, vehLenPx, 12);

          break;
      }

      if(vv != null) {
        ac_.getGUI().getMapPanel().getDrawItems().addItem(key, vv, "a");
        if(ac_.getGUI().getMapPanel().getCanvas().getCC().getRange().intersects(vv.getBoundingBox())) //contains(pt[0].x, pt[0].y))
          visibleVVs.add(vv);
      }
    }
    if(!visibleVVs.isEmpty()) {
      ac_.getGUI().getMapPanel().clearPointsOfInterest();
      ac_.getGUI().getMapPanel().setPointsOfInterest(visibleVVs);
    }
    ac_.getGUI().getMapPanel().refresh(false, false, true);
  }

  public void recordMovie(double start, double end, double speed) {
    int width = ac_.getGUI().getMapPanel().getWidth(), height = ac_.getGUI().getMapPanel().getHeight();
    double fps = 24;
    double movieLenS = (end-start) / speed;
    double frameCount = fps * movieLenS;
    double step = (end-start) / frameCount;

    // Generate the output media locators.
    MediaLocator oml;

    String outputURL = "file:/home/demory/5p/test.mov";
    if ((oml = new MediaLocator(outputURL)) == null) {
	    System.err.println("Cannot build media locator from: " + outputURL);
	    return;
    }

    MovieRecorder recorder = new MovieRecorder();

    recorder.run(width, height, (int) fps, oml, (int) start, (int) step, (int) frameCount, this);

  }

  public void getFrame(int stime) {
    try {
      refreshSnapshot(stime);
      BufferedImage img = new BufferedImage(ac_.getGUI().getMapPanel().getWidth(), ac_.getGUI().getMapPanel().getHeight(), BufferedImage.TYPE_INT_RGB);
      Graphics2D g = img.createGraphics();
      ac_.getGUI().getMapPanel().paintAll(g);
      ImageIO.write(img, "jpeg", new File("/home/demory/5p/temp/foo.jpg"));
      //return img;
    } catch (IOException ex) {
      Logger.getLogger(Visualizer.class.getName()).log(Level.SEVERE, null, ex);
    }
    //return img;
  }
  // ANIMATION METHODS
  public void initAnimation(int start, int end, int speed) {
    if (animation_ != null) {
      ac_.getGUI().getMapPanel().setIsAnimating(false);
      animation_.pause();
    }
    animation_ = new Animation(start, end, 10, speed, ac_.getGUI(), this);

    /*
    // scan current viewport for subroutes
    Iterator<SubRoute> subs = ac_.getEngine().getRoutes().getSubsFromLinks(ac_.getGUI().getMapPanel().getVisibleLinks());

    // initialize animated runs
    Scenario scen = ac_.getActiveScenario();

    Set<Route> validRtes = new HashSet<Route>();
    for(Route rte : ac_.getEngine().getRoutes().getCollection(scen, true))
      validRtes.add(rte);

    while (subs.hasNext()) {
      SubRoute sub = subs.next();

      if (!validRtes.contains(sub.getRoute())) {
        continue;
      }

      if (!sub.hasTable(service)) {
        continue;
      }
      for (int r = 1; r <= sub.getRunCount(service); r++) {
        int runStart = sub.getTime(service, r, 0);
        int runEnd = sub.getTime(service, r, sub.stopCount() - 1);
        if (runEnd <= start || runStart >= end) {
          continue;
        }
        runStart = Math.max(start, runStart);
        runEnd = Math.min(end, runEnd);
        animation_.addItem(new AnimatedRun(sub, service, r, runStart, runEnd));
        System.out.println("init run: " + sub.getFullID() + "/" + r + " " + FPUtil.sTimeToStr(runStart) + " to " + FPUtil.sTimeToStr(runEnd));
      }
    }*/
  }
}
